package org.geppeto.eva.synchronizer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
import java.lang.Thread;

public class Token {
	protected Semaphore _sem;
	protected Mailbox _mb;
	private InstanceManager _inst;
	private String _key;
	private String _currNmbr;
	private HashMap consumerWaitTimer = new HashMap();
	private ArrayList<Timer> calling_timer = new ArrayList<Timer>();
	public abstract class MyTimerTask extends TimerTask {
		protected InstanceManager _theMngr;
		protected String _theKey;
		public MyTimerTask(InstanceManager mngr, String key){
			super();
			_theMngr = mngr;
			_theKey = key;
		}
	}	
	private MyTimerTask task;
	
	public static int CALLING_TIME = 15; 	
	public Token(Semaphore sem, Mailbox mb, InstanceManager inst, String key){
		_sem = sem;
		_mb = mb;
		_currNmbr = "0"; //Initial value being displayed
		_inst = inst;
		_key = key;
		task = new MyTimerTask(_inst, _key) {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				try{				
					_theMngr.bye(_theKey, InstanceManager.TOKEN_CALLBACK + _theKey);
					}
					catch (Exception ex){
						int a = 2;
					}
			}
		};
	}

	protected HashMap takeNmbr(String userID){
		try{
			HashMap result = new HashMap();
			String res_sem = _sem.release(userID);
			String nmbr_released = getStringBetween(res_sem, ": {\"counter\": \"", "\"}}");
			if (res_sem.equals("none")) {
				result.put("tok", "Geting a number failed");
				result.put("sem", "");
				result.put("mb", "");
				return result;
			}
			//Put nmbr_released from sem into mb
			String res_mb = _mb.putMessage(userID, nmbr_released);
			if (res_mb.equals("Failed")) {
				result.put("tok", "Putting a number failed");  
				result.put("sem", "");
				result.put("mb", "");
				return result;
			}		
			
			//Result for token operation
			String res_tok = "{\"description\": \"token_issued\", " +
			"\"modification\": {\"tokenNmbr\": \"" +
			nmbr_released +
			"\"}}";
			this._currNmbr = nmbr_released;
			
			result.put("tok", res_tok);
			result.put("sem", res_sem);
			result.put("mb", res_mb);
			return result;
		} catch(Exception ex){
			HashMap result = new HashMap();
			result.put("tok", "Exception");
			result.put("sem", "Exception");
			result.put("mb", "Exception");
			return result;
		}
	}

	protected HashMap bye( String userID,  String key){
		try{
			HashMap result=new HashMap();
			
			//From mb get the nmbr of next consumer to be served
			String res_mb = _mb.getMessage(InstanceManager.TOKEN_CALLBACK + key);			
			if (res_mb.equals("Failed")) {
				result.put("tok", "Calling a new number failed");
				result.put("mb", "");
				return result;
			}
			
			if (calling_timer.size()>1){
				//Delete old timer, new one will be inserted,
				//And current one will become old in next round
				calling_timer.get(1).cancel();
				calling_timer.remove(1);
			}
			
			String msgText = getStringBetween(res_mb, "\"msgText\": \"", "\"}}");
			//Mozda nema msgText, nego je salter otisao u red cekanja
			//POkriti taj slucaj da prodje ovdje, a callbacToken ce mu onda javit poruku
			if (msgText.equals("none")){
				String res_tok = "{\"description\": \"nothing_to_display\", " +
				"\"modification\": {\"displayMsg\": \"" + "idle" + 
				"\"}}";
				result.put("tok", res_tok);
				result.put("mb", res_mb);
				return result;
			}
			String res_tok = "{\"description\": \"token_to_display\", " +
			"\"modification\": {\"callingNmbr\": \"" + msgText + "\", " +
			"\"waiting_in_sec\": \"" + Integer.toString(CALLING_TIME) +
			"\"}}";
			this._currNmbr = msgText;
			
			result.put("tok", res_tok);
			result.put("mb", res_mb);

			
			Timer time = new Timer();
			calling_timer.add(0, time);
			
			MyTimerTask newTask = new MyTimerTask(_inst, _key) {
				
				@Override
				public void run() {
					// TODO Auto-generated method stub
					try{				
						_theMngr.bye(_theKey, InstanceManager.TOKEN_CALLBACK + _theKey);
						}
						catch (Exception ex){
							int a = 2;
						}
				}
			};
			time.schedule(newTask, CALLING_TIME * 1000);

			return result;
		} catch (Exception ex){
			HashMap result = new HashMap();
			result.put("tok", "Exception");
			result.put("mb", "Exception");
			return result;
		}		
	}	
	
	protected String callbackToken(String msg, String key){
		try {
			String result = "";
			result  = "{\"description\": \"token_to_display\", " +
			"\"modification\": {\"callingNmbr\": \"" + msg + "\", " +
			"\"waiting_in_sec\": \"" + Integer.toString(CALLING_TIME) +
			"\"}}";
			this._currNmbr = msg;
			
			//Start timer			
			Timer time = new Timer();
			calling_timer.add(0, time);
			
			MyTimerTask newTask = new MyTimerTask(_inst, _key) {				
				@Override
				public void run() {
					// TODO Auto-generated method stub
					try{				
						_theMngr.bye(_theKey, InstanceManager.TOKEN_CALLBACK + _theKey);
						}
						catch (Exception ex){
							int a = 2;
						}
				}
			};
			time.schedule(newTask, CALLING_TIME * 1000);
			
			return result;			
		} catch (Exception ex) {
			return "Failed";
		}
	}


	private String getInfo(){
		String result = "";
		return result;
	}
	
	private String getStringBetween(String str, String begin, String end){
		try{
			int isThere = str.indexOf(begin);
			if (isThere>-1)
				return str.substring(isThere + 	begin.length(), str.indexOf(end));
			else
				return "none";
		} catch(Exception ex) {
			return "none";
		}
	}
	
	private String stringifyArrayList2JSON(ArrayList<String> li){
		//http://www.sigpwned.com/projects/jsonification
		String result="[";
		int i=0;
		Iterator<String> itr = li.iterator();
	    while (itr.hasNext()){	    	
	    	if (i != 0) result+=", ";
	    	result += "\"" + itr.next() + "\"";
	    	i++;
	    }
	    result+="]";
	    return result;
	}

	public String hello(String userID, String key, String nmbr) {
		if (!nmbr.equals(this._currNmbr))
			return "Nope";
		calling_timer.get(0).cancel();		
		//Moguce je da se pojavio onaj koji je zakasnio na timeru, 
		// ali nitko drugi nije dosao u medjuvremenu
		// i onda je citac vec usao u cekanje dolaska novog broja. 
		// Tada treba otkazati to cekanje iz mbx-a, 
		// iz stanja cekanje klijenta preci u aktivnu obradu nakon koje ide bye
		// 
		String mbInfo = _mb.getInfo();
		String countReaders = getStringBetween(mbInfo, "\"readerCnt\": \"", "\", " +
				"\"list\": ");
		if (countReaders.equals("none")) // visak || !countReaders.equals("1"))
			return "Hello";
		String list = getStringBetween(mbInfo, "\", " +	"\"list\": ", "\"}}");
		if (list.contains(InstanceManager.TOKEN_CALLBACK)){
			_mb.putMessage(InstanceManager.TOKEN_CALLBACK, "getOut. Lost sheep has showed up.");
		}
		return "Hello";
	}
}
